<?php
/**
 * +------------------------------
 * Created by PhpStorm.
 * +------------------------------
 * User: xxx
 * +------------------------------
 * DateTime: 2019/12/4 20:23
 * +------------------------------
 */

namespace Core;


use DI\Container;
use DI\ContainerBuilder;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;

class BeanFactory
{

    private static $env = [];

    /**
     * @var Container
     */
    private static $container;

    private static $handlers = [];

    /**
     * 初始化
     * @throws \Exception
     */
    public static function init()
    {
        //取得属性参数设置
        self::$env = parse_ini_file(ROOT_PATH . '/.env');

        $builder = new ContainerBuilder();
        $builder->useAnnotations(true);
        self::$container = $builder->build();


        //取得注解处理方法
//        self::$handlers = require_once ROOT_PATH . '/core/annotations/AnnotationHandlers.php';
        $handlers = glob(ROOT_PATH . "/core/annotationhandlers/*.php");
        foreach ($handlers as $handler) {
            self::$handlers = array_merge(self::$handlers, require_once $handler);
        }

        //注册annotation的命名空间 ***此方法在linux下有问题
//        AnnotationRegistry::registerAutoloadNamespace("Core\\annotations");
        $loader = require ROOT_PATH . '/vendor/autoload.php';
        AnnotationRegistry::registerLoader([$loader, 'loadClass']);

        //扫描类上注解并解析-类注解-方法注解-属性注解 并放入容器
//        $scan_dir = self::getEnv('scan_dir', ROOT_PATH . '/app');
//        $scan_root_namespace = self::getEnv('scan_root_namespace', "App\\");
//        self::scanBeans($scan_dir, $scan_root_namespace);

        // 上面的方法改进如下： 多目录扫描处理注解，并加入DI容器
        //数组格式： 目录绝对路径 => 目录命名空间
        $scans = [
            ROOT_PATH . '/core/init' => "Core\\",
            self::getEnv('scan_dir', ROOT_PATH . '/app') => self::getEnv('scan_root_namespace', "App\\")
        ];

        foreach ($scans as $scan_dir => $namespace) {
            self::scanBeans($scan_dir, $namespace);
        }


    }

    /**
     * 递归取得所有存在注解的php文件
     * @param $dir
     * @return array
     */
    private static function getAllBeanFiles($dir)
    {
        $phpfiles = [];
        //加载此；路径下所有php文件
        $files = glob($dir . '/*');
        foreach ($files as $file) {

            $fileinfo = is_file($file) ? pathinfo($file) : [];

            if (is_dir($file)) {

                //如果是文件夹则递归回来的结果做合并处理
                $phpfiles = array_merge($phpfiles, self::getAllBeanFiles($file));
            } elseif (isset($fileinfo['extension']) && $fileinfo['extension'] == 'php') {
                $phpfiles[] = $file;
            }
        }
        return $phpfiles;
    }


    /**
     * 扫描处理注解
     * @throws \DI\DependencyException
     * @throws \DI\NotFoundException
     * @throws \Doctrine\Common\Annotations\AnnotationException
     * @throws \ReflectionException
     */
    public static function scanBeans($scan_dir, $scan_root_namespace)
    {
        //加载此；路径下所有php文件,  递归支持多级目录扫描
        $phpfiles = self::getAllBeanFiles($scan_dir);
        foreach ($phpfiles as $phpfile) {
            require_once $phpfile;
        }

        $reader = new AnnotationReader(); //实例化reader对象 用来读取注解定义

        //取得所有定义的类 并过滤, 使用反射实例化
        foreach (get_declared_classes() as $class) {

            if (strstr($class, $scan_root_namespace)) {

                $ref_class = new \ReflectionClass($class); //目标类的反射对象
                $class_annos = $reader->getClassAnnotations($ref_class);

                foreach ($class_annos as $class_anno) {
                    //如果没有做注解解析，则过滤掉
                    if (!isset(self::$handlers[get_class($class_anno)])) continue;

                    //获取handler处理过程
                    $handler = self::$handlers[get_class($class_anno)];
                    $instance = self::$container->get($ref_class->getName());

                    //处理属性注解：
                    self::handlePropAnno($instance, $ref_class, $reader);

                    //处理方法注解：
                    self::handleMethodAnno($instance, $ref_class, $reader);

                    //处理类(对象)注解：参数1 实例对象  参数2 容器对象  参数3 Bean对象
                    $handler($instance, self::$container, $class_anno); //执行处理
                }

            }
        }
        return;
    }

    /**
     * 处理属性注解
     * @param $instance
     * @param \ReflectionClass $ref_class
     * @param AnnotationReader $reader
     */
    private static function handlePropAnno(&$instance, \ReflectionClass $ref_class, AnnotationReader $reader)
    {
        $props = $ref_class->getProperties(); //取得反射对象所有属性
        foreach ($props as $prop) {
            $prop_annos = $reader->getPropertyAnnotations($prop);
            foreach ($prop_annos as $prop_anno) {
                //如果没有做注解解析，则过滤掉
                if (!isset(self::$handlers[get_class($prop_anno)])) continue;

                $handler = self::$handlers[get_class($prop_anno)];
                $instance = $handler($prop, $instance, $prop_anno); //处理属性注解
            }
        }
    }

    /**
     * 处理方法注解
     * @param $instance
     * @param \ReflectionClass $ref_class
     * @param AnnotationReader $reader
     */
    private static function handleMethodAnno(&$instance, \ReflectionClass $ref_class, AnnotationReader $reader)
    {
        $methods = $ref_class->getMethods(); //取得反射对象所有属性
        foreach ($methods as $method) {
            $method_annos = $reader->getMethodAnnotations($method);
            foreach ($method_annos as $method_anno) {
                //如果没有做注解解析，则过滤掉
                if (!isset(self::$handlers[get_class($method_anno)])) continue;

                $handler = self::$handlers[get_class($method_anno)];
                $instance = $handler($method, $instance, $method_anno); //处理方法注解
            }
        }
    }

    /**
     * 从容器取得实例
     * @param string $name
     * @return bool|mixed
     */
    public static function getBean(string $name)
    {
        try {
            return self::$container->get($name) ?? null;
        } catch (\Exception $e) {

            //return false 会屏蔽异常报错 需要处理掉
            return false;

            //            echo $e->getMessage();
            //            echo $e->getTraceAsString();
        }


    }

    /**
     * 设置实例到容器
     * @param string $name
     * @param null $instance
     */
    public static function setBean(string $name, $instance = null)
    {
        return self::$container->set($name, $instance);
    }

    /**
     * 取得参数
     * @param string $key
     * @param string $default
     * @return mixed|string
     */
    private static function getEnv(string $key, string $default = '')
    {
        if (isset(self::$env[$key])) return self::$env[$key];
        return $default;
    }


}

































